package guicommon;

import guicommon.controls.handlers.EventMessage;
import guicommon.gui.*;
import guicommon.session.*;
import java.lang.reflect.Constructor;
import java.nio.CharBuffer;
import java.util.Hashtable;

public class MessageFactory {

    private static MessageFactory instance;

    public static MessageFactory GetInstance() {
        if (instance == null) {
            instance = new MessageFactory();
        }
        return instance;
    }

    private static Hashtable<Integer, Class> sessionMessageTypes, handlersMessagesTypes, guiMssageTypes;

    private static Hashtable<MessageType, Hashtable<Integer, Class>> mapMessageTypeToDictionary;

    private final Class[] paramTypesForRecvConstructor = new Class[] { Header.class, CharBuffer.class };
            //paramTypesForSendConstructor = new Class[] { Header.class };

    private MessageFactory() {
        sessionMessageTypes = new Hashtable();
        handlersMessagesTypes = new Hashtable();
        guiMssageTypes = new Hashtable();

        InitializeSessionMessageTypes();
        InitializeHandlerMessageTypes();
        InitializeGUIMessageTypes();

        mapMessageTypeToDictionary = new Hashtable();
        mapMessageTypeToDictionary.put(MessageType.Session, sessionMessageTypes);
        mapMessageTypeToDictionary.put(MessageType.Handler, handlersMessagesTypes);
        mapMessageTypeToDictionary.put(MessageType.GUI, guiMssageTypes);
    }

    private void InitializeSessionMessageTypes() {
        sessionMessageTypes.put(ConnectMessage.ID, ConnectMessage.class);
        sessionMessageTypes.put(DisconnectMessage.ID, DisconnectMessage.class);
        sessionMessageTypes.put(ErrorMessage.ID, ErrorMessage.class);
    }

    private void InitializeHandlerMessageTypes() {
        handlersMessagesTypes.put(EventMessage.ID, EventMessage.class);
    }

    private void InitializeGUIMessageTypes() {
        guiMssageTypes.put(CallBackControlMessage.ID, CallBackControlMessage.class);
        guiMssageTypes.put(EmptyControlMessage.ID, EmptyControlMessage.class);
        guiMssageTypes.put(WindowMessage.ID, WindowMessage.class);
        guiMssageTypes.put(ButtonMessage.ID, ButtonMessage.class);
        guiMssageTypes.put(LabelMessage.ID, LabelMessage.class);
        guiMssageTypes.put(TextBoxMessage.ID, TextBoxMessage.class);

        guiMssageTypes.put(UpdateMessage.ID, UpdateMessage.class);
        guiMssageTypes.put(TextAreaMessage.ID, TextAreaMessage.class);
    }

    Message CreateMessage(Header header, CharBuffer buffer) throws Exception {
         Hashtable<Integer, Class> needDictionary = mapMessageTypeToDictionary.get(header.GetMessageType());
         Class type = needDictionary.get(header.GetMessageCode());
         Constructor constructor  = type.getDeclaredConstructor(paramTypesForRecvConstructor);
         if(!constructor.isAccessible()) {
             constructor.setAccessible(true);
         }
         Message result = (Message) constructor.newInstance(header, buffer);
         return  result;
    }

    public MessageType GetMessageTypeFromMessage(Class type) {
        for (MessageType mType : mapMessageTypeToDictionary.keySet()) {
            Hashtable<Integer, Class> currentDictionary = mapMessageTypeToDictionary.get(mType);
            for (Integer code : currentDictionary.keySet()) {
                if (currentDictionary.get(code).equals(type)) {
                    return mType;
                }
            }
        }
        return MessageType.Session;
    }
}